2025ICPC 亚洲区域赛(南京)题解
B - What, More Kangaroos?
Question
一条数轴上有
- 按键1:移动到
- 按键2:移动到
- 按键3:移动到
- 按键4:移动到
你可以按键任意次,问最大化操作后具有负坐标的袋鼠的数量
Solution
很容易可以看出这个题是需要求一个
由于
假设答案为
于是问题就转化为
按照极角排序后统计即可
Code
#include <bits/stdc++.h>
using namespace std;
void solve() {
int n; cin >> n;
map<long double, int> mp;
int now = 0;
for (int i = 0; i < n; i++) {
int a, b, c;
cin >> a >> b >> c;
if (a == 0 && b == 0) { continue; }
long double s = atan2l(a, -b); // 必须long double
long double t = atan2l(-a, b);
mp[s]++;
mp[t]--;
if (s > t) {
now++;
}
}
int ans = now;
for (auto _ : mp) {
int delta = _.second;
now += delta;
ans = max(ans, now);
}
cout << ans << '\n';
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
int t; cin >> t;
while (t--) solve();
return 0;
}
C - Distributing Candies(签到题)
队友写的签到
Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll Tex, n;
void AC() {
cin >> n;
if (n & 1) cout << "No\n";
else cout << "Yes\n" << n / 2 << " " << n / 2 << "\n";
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
cin >> Tex;
while (Tex --) AC();
return 0;
}
F - Bitwise And Path
Question
给定一个初始没有边的
1 u v w在 u,v 之间连一条边权为 w 的边2 u v定义一条路径的的值为路径上边权的按位与的值,求 u,v 之间最长路径的值
Solution
考虑建
先考虑查询,相当于倍增的写法,从高到低查询每一位是否可行
然后考虑合并,一个暴力的想法是,如果我要添加一条边权为
其实有很多重复操作,而且子集关系具有传递性
我们可以提前预处理出一颗树,一个节点的儿子节点都是其子集,当处理到一个节点上的
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll MAXN = 1e3 + 5;
ll Tex, f[(1 << 12) + 2][MAXN], n, q, vis[(1 << 12) + 2];
vector<ll> mp[(1 << 12) + 2];
void init_set(ll fa[]) {
for (int i = 0; i <= n; i ++) {
fa[i] = i;
}
}
ll find_set(ll fa[], ll x) {
return fa[x] = fa[x] == x ? fa[x] : find_set(fa, fa[x]);
}
void union_set(ll fa[], ll x, ll y) {
x = find_set(fa, x);
y = find_set(fa, y);
if (x == y) return;
fa[x] = y;
}
void dfs(ll val, ll x, ll y) {
x = find_set(f[val], x);
y = find_set(f[val], y);
if (x == y) return;
union_set (f[val], x, y);
for (auto it : mp[val]) {
dfs (it, x, y);
}
}
void AC() {
cin >> n >> q;
for (int i = 0; i < (1 << 12); i ++) {
init_set(f[i]);
}
ll ans = 0;
while (q --) {
char ch;
ll x, y, w;
cin >> ch >> x >> y;
if (ch == '+') {
cin >> w;
dfs(w, x, y);
}
else {
ll dq = 0;
for (int i = 11; i >= 0; i --) {
ll xx = find_set (f[dq | (1 << i)], x);
ll yy = find_set (f[dq | (1 << i)], y);
if (xx == yy) dq |= (1 << i);
}
if (find_set(f[0], x) != find_set(f[0], y)) dq = -1;
ans += dq;
}
// cout << q << "\n";
}
cout << ans << "\n";
}
void calc(ll x) {
vis[x] = 1;
// cout << x << "\n";
for (int i = 0; i < 12; i ++) {
if ((x >> i) & 1) {
ll xx = x - (1 << i);
mp[x].push_back(xx);
if (vis[xx]) continue;
calc(xx);
}
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
calc((1 << 12) - 1);
cin >> Tex;
while (Tex --) AC();
return 0;
}
G - Bucket Bonanza
Question
有
你可以在第
有
Solution
思维好题,摘至官方题解
先不考虑有的水桶会漏完的情况。合并两个水桶等价于删除两者间较小的容量和较大的漏水率。可以发现答案和水桶容量-漏水对应关系无关,只和容量集合和漏水集合有关
对于每次询问,考虑贪心,每次尝试合并容量最小的水桶和漏水最多的水桶,直到最小容量大于最多漏水量。如果容量最小和漏水最多是同一个水桶,这个水桶不可能储水,等价于删除
接下来考虑有的水桶会漏完的情况,此时把漏完的水桶和其它桶合并,答案不会变得更差。所以至少存在一个最优答案是水漏不完的(或只保留 0 个水桶),直接用之前的贪心解决即可
所以,每次询问二分删多少个水桶即可
Code
#include <bits/stdc++.h>
using namespace std;
#define int long long
using ll = long long;
void solve() {
int n; cin >> n;
vector<int> a(n + 1, 0), b(n + 1, 0);
for (int i = 1; i <= n; i++) cin >> a[i];
for (int i = 1; i <= n; i++) cin >> b[i];
sort(a.begin() + 1, a.end());
reverse(a.begin() + 1, a.end());
sort(b.begin() + 1, b.end());
vector<ll> sum_a(n + 1, 0), sum_b(n + 1,0);
for (int i = 1; i <= n; i++) sum_a[i] = sum_a[i - 1] + a[i];
for (int i = 1; i <= n; i++) sum_b[i] = sum_b[i - 1] + b[i];
int q; cin >> q;
while (q--) {
int t; cin >> t;
int l = 1, r = n + 1;
while (l + 1 < r) {
int mid = (r + l) / 2;
if (a[mid] - t * b[mid] >= 0) l = mid;
else r = mid;
}
cout << max(sum_a[l] - 1ll * t * sum_b[l], 0ll) << ' ';
}
cout << '\n';
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
int t; cin >> t;
while (t--) solve();
return 0;
}
K - Xiangqi
Question
神秘象棋题
中国象棋棋盘上有一个车和一个马,给出车和马的位置,此时马先走
问,车是否能吃掉马
Solution
神秘结论题,如果车不能在马走了一步之后然后抓到马,那么就永远抓不死马